package com.kh.rationaldrugreview.service;

import com.kh.rationaldrugreview.dto.EntityMatchResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 混合实体匹配服务（核心协调器）
 * 实现缓存 -> 传统算法 -> LLM的分层匹配策略
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class HybridEntityMatcher {
    
    private final EntityMatchingService traditionalMatcher;
    private final LLMEntityMatcher llmMatcher;
    
    // L1缓存：内存缓存
    private final Map<String, CachedMatchResult> matchCache = new ConcurrentHashMap<>();
    
    // 配置参数
    @Value("${entity-matching.cache.enabled:true}")
    private boolean cacheEnabled;
    
    @Value("${entity-matching.cache.ttl:86400}")
    private long cacheTtlSeconds;
    
    @Value("${entity-matching.traditional.min-confidence:0.85}")
    private double traditionalMinConfidence;
    
    @Value("${entity-matching.llm.enabled:true}")
    private boolean llmEnabled;
    
    @Value("${entity-matching.llm.threshold:0.70}")
    private double llmThreshold;
    
    /**
     * 缓存的匹配结果
     */
    private static class CachedMatchResult {
        EntityMatchResult result;
        LocalDateTime cachedAt;
        long hitCount;
        
        boolean isExpired(long ttlSeconds) {
            return cachedAt.plusSeconds(ttlSeconds).isBefore(LocalDateTime.now());
        }
    }
    
    /**
     * 执行混合匹配策略
     */
    public EntityMatchResult match(String inputName, String entityType) {
        long startTime = System.currentTimeMillis();
        
        // 输入验证
        if (inputName == null || inputName.trim().isEmpty()) {
            return EntityMatchResult.notFound(inputName, entityType);
        }
        
        String normalizedInput = inputName.trim();
        
        // 1. 查询缓存
        if (cacheEnabled) {
            EntityMatchResult cachedResult = getCachedResult(normalizedInput, entityType);
            if (cachedResult != null) {
                log.debug("Cache hit for '{}' -> {}", normalizedInput, cachedResult.getCanonicalName());
                cachedResult.setProcessingTimeMs(System.currentTimeMillis() - startTime);
                return cachedResult;
            }
        }
        
        // 2. 传统算法匹配
        EntityMatchResult traditionalResult = traditionalMatcher.matchEntity(normalizedInput, entityType);
        
        // 高置信度：直接返回并缓存
        if (traditionalResult.isMatched() && traditionalResult.getConfidence() >= traditionalMinConfidence) {
            log.info("Traditional match successful for '{}' with confidence {}", 
                normalizedInput, traditionalResult.getConfidence());
            
            cacheResult(normalizedInput, entityType, traditionalResult);
            traditionalResult.setProcessingTimeMs(System.currentTimeMillis() - startTime);
            return traditionalResult;
        }
        
        // 中等置信度：返回但标记需要确认
        if (traditionalResult.isMatched() && traditionalResult.getConfidence() >= llmThreshold) {
            log.info("Traditional match found for '{}' with medium confidence {}", 
                normalizedInput, traditionalResult.getConfidence());
            
            traditionalResult.setNeedsConfirmation(true);
            cacheResult(normalizedInput, entityType, traditionalResult);
            traditionalResult.setProcessingTimeMs(System.currentTimeMillis() - startTime);
            return traditionalResult;
        }
        
        // 3. LLM匹配（低置信度或未匹配）
        if (llmEnabled && (!traditionalResult.isMatched() || traditionalResult.getConfidence() < llmThreshold)) {
            log.info("Falling back to LLM matching for '{}'", normalizedInput);
            
            try {
                // 传递传统算法的候选结果给LLM
                List<EntityMatchResult.CandidateEntity> candidates = traditionalResult.getCandidates();
                EntityMatchResult llmResult = llmMatcher.matchWithLLM(normalizedInput, entityType, candidates);
                
                if (llmResult.isMatched()) {
                    log.info("LLM match successful for '{}' -> {}", 
                        normalizedInput, llmResult.getCanonicalName());
                    
                    // 合并匹配详情
                    if (traditionalResult.getDetails() != null && llmResult.getDetails() != null) {
                        llmResult.getDetails().getMatchingSteps().addAll(0, 
                            traditionalResult.getDetails().getMatchingSteps());
                    }
                    
                    cacheResult(normalizedInput, entityType, llmResult);
                    llmResult.setProcessingTimeMs(System.currentTimeMillis() - startTime);
                    return llmResult;
                }
                
            } catch (Exception e) {
                log.error("LLM matching failed, using traditional result", e);
            }
        }
        
        // 4. 返回传统算法结果（即使是未匹配）
        cacheResult(normalizedInput, entityType, traditionalResult);
        traditionalResult.setProcessingTimeMs(System.currentTimeMillis() - startTime);
        return traditionalResult;
    }
    
    /**
     * 批量匹配
     */
    public List<EntityMatchResult> batchMatch(List<String> inputNames, String entityType) {
        return inputNames.stream()
            .map(name -> match(name, entityType))
            .toList();
    }
    
    /**
     * 带上下文的匹配
     */
    public EntityMatchResult matchWithContext(String description, Map<String, Object> context, String entityType) {
        // 先尝试直接匹配
        EntityMatchResult directMatch = match(description, entityType);
        
        if (directMatch.isHighConfidence()) {
            return directMatch;
        }
        
        // 使用LLM进行上下文匹配
        if (llmEnabled) {
            try {
                EntityMatchResult contextMatch = llmMatcher.matchWithContext(description, context, entityType);
                if (contextMatch.isMatched() && contextMatch.getConfidence() > directMatch.getConfidence()) {
                    return contextMatch;
                }
            } catch (Exception e) {
                log.error("Context matching failed", e);
            }
        }
        
        return directMatch;
    }
    
    /**
     * 获取缓存结果
     */
    private EntityMatchResult getCachedResult(String inputName, String entityType) {
        String cacheKey = buildCacheKey(inputName, entityType);
        CachedMatchResult cached = matchCache.get(cacheKey);
        
        if (cached != null) {
            if (!cached.isExpired(cacheTtlSeconds)) {
                cached.hitCount++;
                
                // 克隆结果避免修改缓存
                EntityMatchResult result = cloneResult(cached.result);
                
                // 添加缓存信息
                if (result.getDetails() == null) {
                    result.setDetails(EntityMatchResult.MatchDetails.builder().build());
                }
                result.getDetails().setCacheInfo(EntityMatchResult.CacheInfo.builder()
                    .cacheHit(true)
                    .cacheKey(cacheKey)
                    .cachedAt(cached.cachedAt)
                    .ttlSeconds(cacheTtlSeconds)
                    .build());
                
                return result;
            } else {
                // 过期，移除
                matchCache.remove(cacheKey);
            }
        }
        
        return null;
    }
    
    /**
     * 缓存匹配结果
     */
    private void cacheResult(String inputName, String entityType, EntityMatchResult result) {
        if (!cacheEnabled) {
            return;
        }
        
        String cacheKey = buildCacheKey(inputName, entityType);
        
        CachedMatchResult cached = new CachedMatchResult();
        cached.result = result;
        cached.cachedAt = LocalDateTime.now();
        cached.hitCount = 0;
        
        matchCache.put(cacheKey, cached);
        
        // 定期清理过期缓存
        if (matchCache.size() > 10000) {
            cleanExpiredCache();
        }
    }
    
    /**
     * 构建缓存键
     */
    private String buildCacheKey(String inputName, String entityType) {
        return entityType + ":" + inputName.toLowerCase();
    }
    
    /**
     * 克隆匹配结果
     */
    private EntityMatchResult cloneResult(EntityMatchResult original) {
        return EntityMatchResult.builder()
            .originalName(original.getOriginalName())
            .matchedEntity(original.getMatchedEntity())
            .canonicalName(original.getCanonicalName())
            .entityId(original.getEntityId())
            .entityType(original.getEntityType())
            .confidence(original.getConfidence())
            .matchType(original.getMatchType())
            .algorithm(original.getAlgorithm())
            .candidates(original.getCandidates())
            .details(original.getDetails())
            .needsConfirmation(original.getNeedsConfirmation())
            .matchedAt(original.getMatchedAt())
            .errorMessage(original.getErrorMessage())
            .build();
    }
    
    /**
     * 清理过期缓存
     */
    private void cleanExpiredCache() {
        matchCache.entrySet().removeIf(entry -> 
            entry.getValue().isExpired(cacheTtlSeconds));
        log.debug("Cleaned expired cache entries, current size: {}", matchCache.size());
    }
    
    /**
     * 获取缓存统计
     */
    public Map<String, Object> getCacheStatistics() {
        long totalHits = matchCache.values().stream()
            .mapToLong(c -> c.hitCount)
            .sum();
        
        return Map.of(
            "cacheSize", matchCache.size(),
            "totalHits", totalHits,
            "cacheEnabled", cacheEnabled,
            "ttlSeconds", cacheTtlSeconds
        );
    }
    
    /**
     * 清空缓存
     */
    public void clearCache() {
        matchCache.clear();
        log.info("Entity match cache cleared");
    }
    
    /**
     * 预热缓存（加载常用实体）
     */
    public void warmupCache(List<String> commonNames, String entityType) {
        log.info("Warming up cache with {} common names", commonNames.size());
        
        for (String name : commonNames) {
            try {
                match(name, entityType);
            } catch (Exception e) {
                log.warn("Failed to warm up cache for: {}", name, e);
            }
        }
        
        log.info("Cache warmup completed");
    }
}